package com.tencent.welink.iot;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.tencent.welink.sm.SM4Utils;
import com.tencent.welink.util.CrontabUtil;
import com.tencent.welink.util.RandomUtil;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.eclipse.paho.client.mqttv3.*;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by junchihe on 2020/6/11.
 */
public class WelinkMqtt {
    private static final Logger log = LogManager.getLogger(WelinkMqtt.class);

    private   String HOST ;
    private   String din ;
    public   String TOPIC ;
    private   String TOPIC_PUSH;
    private   String clientid ;
    private String wId;

    public MqttClient client;
    private MqttTopic topicPush;
    private String userName = "welink_client";
    private String passWord = "rAElRzKIvX_6EZqEWIvKNr_ILTm7*V3Se2v47TeaJACquKx40PtsUz0LeLUlfEX6";

    private MqttMessage message;
    protected LoginData loginData;

    protected List<SubDeviceDTO> subDeviceDTOList = new ArrayList<>();

    public WelinkMqtt(String HOST,String din,String TOPIC,String TOPIC_PUSH,String userName,String passWord,String clientid,LoginData loginData,String wId) throws MqttException {
        // MemoryPersistence设置clientid的保存形式，默认为以内存保存
        this.HOST = HOST;
        this.din = din;
        this.TOPIC = "/"+ StringUtils.strip(TOPIC,"/")+"/"+din;
        this.TOPIC_PUSH = "/"+ StringUtils.strip(TOPIC_PUSH,"/")+"/"+din;
        this.userName = userName;
        this.passWord = passWord;
        this.clientid=clientid+"_" + RandomUtil.getRandomInt();
        this.loginData = loginData;
        this.wId = wId;
        client = new MqttClient(HOST, this.clientid, new MemoryPersistence());
        log.info("host={},topic={},topic_push={},clientid={}",this.HOST,this.TOPIC,this.TOPIC_PUSH,this.clientid);
    }

    /**
     * mqtt初始化
     */
    public void startConnect(){
        if(connect()){
            scheduleSendHeart();
            sendSubDeviceRegister();
            scheduleGetSubDevice();
            scheduleSendSubDeviceHeart();
        }
    }

    /**
     * 定时发送心跳
     */
    private void scheduleSendHeart(){
        CrontabUtil.addTaskInRate(()->{
            try {
                this.sendMessage(loginData.load(), 30001, "welink", true);
            } catch (Exception e){
                log.error("发送mqtt消息失败：",e);
                //System.exit(0);
            }
        },20000,true);
    }


    /**
     * 连接mqtt
     * @return
     */
    private boolean connect() {
        boolean success = false;
        MqttConnectOptions options = new MqttConnectOptions();
        options.setCleanSession(true);
        options.setUserName(userName);
        options.setPassword(passWord.toCharArray());
        // 设置超时时间
        options.setConnectionTimeout(10);
        // 设置会话心跳时间
        options.setKeepAliveInterval(20);
        options.setAutomaticReconnect(true);
        try {
            client.setCallback(new MQTTMessageReceive(this));
            client.connect(options);
//            topic = client.getTopic(TOPIC);
            topicPush = client.getTopic(TOPIC_PUSH);
            //订阅消息
            int[] Qos  = {1};
            String[] serverTopic = {TOPIC};
            client.subscribe(serverTopic, Qos);

            log.info("start server");
            success = true;
        } catch (Exception e) {
            log.error("发送mqtt消息失败：",e);
        }
        return success;
    }

    private void publish(MqttTopic topic , MqttMessage message) throws MqttException {
        MqttDeliveryToken token = topic.publish(message);
        log.info(System.currentTimeMillis()+"push message,topic:"+topic);
        token.waitForCompletion();
        log.info("message is published completely! "
                + token.isComplete());
    }

    public void sendMessage(String secretKey,String iv,JSONObject jsonObject) throws MqttException {
        byte[] encryptData = SM4Utils.encryptDataCBC(jsonObject.toJSONString().getBytes(), secretKey, iv);

        MqttMessage message = new MqttMessage();
        message.setQos(1);
        message.setRetained(true);
        message.setPayload(encryptData);
        this.publish(this.topicPush,message);
    }

    public void sendMessage(String secretKey,String iv,int datapoint,String value,String report) throws MqttException{
        JSONObject msgJson = new JSONObject();
        int seq = RandomUtil.getRandomInt();
        msgJson.put("msgType", report);
        msgJson.put("seq",seq);
        msgJson.put("random", seq);
        msgJson.put("timeStamp", System.currentTimeMillis());
        msgJson.put("datapoint", datapoint);
        msgJson.put("din",din);
        msgJson.put("wId",wId);
        msgJson.put("value", value);
        log.info("send mqtt message:"+msgJson.toString());
        this.sendMessage(secretKey,iv,msgJson);
    }



    public void sendMessage(String secretKey,String iv,int datapoint,long suDin,String value,String report) throws MqttException{
        JSONObject msgJson = new JSONObject();
        int seq = RandomUtil.getRandomInt();
        msgJson.put("msgType", report);
        msgJson.put("seq",seq);
        msgJson.put("random", seq);
        msgJson.put("timeStamp", System.currentTimeMillis());
        msgJson.put("datapoint", datapoint);
        msgJson.put("din",din);
        msgJson.put("subDin",suDin);
        msgJson.put("wId",wId);
        msgJson.put("value", value);
        log.info("send mqtt message:"+msgJson.toString());
        this.sendMessage(secretKey,iv,msgJson);
    }


    public void sendMessage(String secretKey,String iv,int datapoint,long suDin,JSONObject value,String report) throws MqttException{
        JSONObject msgJson = new JSONObject();
        int seq = RandomUtil.getRandomInt();
        msgJson.put("msgType", report);
        msgJson.put("seq",seq);
        msgJson.put("random", seq);
        msgJson.put("timeStamp", System.currentTimeMillis());
        msgJson.put("datapoint", datapoint);
        msgJson.put("din",din);
        msgJson.put("subDin",suDin);
        msgJson.put("wId",wId);
        msgJson.put("value", value);
        log.info("send mqtt message:"+msgJson.toString());
        this.sendMessage(secretKey,iv,msgJson);
    }

    public void sendMessage(LoginData loginData,int datapoint,String value,boolean report) throws MqttException {
        this.sendMessage(loginData.ran,loginData.sm4IV,datapoint,value,report?"report":"ack");
    }


    public void sendMessage(LoginData loginData,int datapoint,JSONObject value,boolean report) throws MqttException {
        this.sendMessage(loginData.ran,loginData.sm4IV,datapoint,value,report?"report":"ack");
    }

    public void sendMessage(String secretKey,String iv,int datapoint,JSONObject value,String report) throws MqttException{
        JSONObject msgJson = new JSONObject();
        int seq = RandomUtil.getRandomInt();
        msgJson.put("msgType", report);
        msgJson.put("seq",seq);
        msgJson.put("random", seq);
        msgJson.put("timeStamp", System.currentTimeMillis());
        msgJson.put("datapoint", datapoint);
        msgJson.put("din",din);
        msgJson.put("wId",wId);
        msgJson.put("value", value);
        log.info("send mqtt message:"+msgJson.toString());
        this.sendMessage(secretKey,iv,msgJson);
    }
    /**
     * 视频网关向后台请求子设备列表消息
     */
    public void requestSubDevice(){
        /**
         * 4.	30007：视频网关向后台请求子设备列表消息
         * a)	din: 视频网关的din
         * b)	timestamp: 当前时间，自1970年1月1日0时起的毫秒数
         * c)	message_id：30007
         * d)	message_content： "welink"
         * e)	msgType： data_report
         */
        try {
            this.sendMessage(loginData,30007,"welink",true);
        } catch (MqttException e) {
            log.error("发送mqtt消息失败：",e);
        }
    }


    /**
     * 注册子设备
     */
    public void sendSubDeviceRegister(){
        /**
         * key	value	description
         * wId	String	网关设备唯一标识
         * msgType	String	report：主动发送，ack：查询返回，固定report
         * seq	Long	消息序号
         * random	Integer	随机数
         * timeStamp	Long	时间戳，单位：毫秒
         * datapoint	Integer	datapoint id，通电后上报dp：30010
         * value	String	消息自定义内容
         * pid	String	产品id
         * sn	String	设备sn
         * page	Integer	第几页
         * pageSize	Integer	一页多少记录,最多100
         * total	Integer	总记录数
         */


        if(this.din.equalsIgnoreCase("300000000000070736")){
            SubDeviceDTO subDeviceDTO = new SubDeviceDTO();
            subDeviceDTO.setPid("1701000301");
            subDeviceDTO.setSn("limyezhaoming000103");
            SubDeviceDTO subDeviceDTO2 = new SubDeviceDTO();
            subDeviceDTO2.setPid("1701000299");
            subDeviceDTO2.setSn("limyezhaoming000102");
            List<SubDeviceDTO> list = new ArrayList<>();
            list.add(subDeviceDTO);
            list.add(subDeviceDTO2);

            JSONObject jsonObject = new JSONObject();
            jsonObject.put("page",1);
            jsonObject.put("pagesize",list.size());
            jsonObject.put("total",list.size());
            JSONArray jsonArray = new JSONArray();
            for(SubDeviceDTO deviceDTO:list){
                JSONObject object = new JSONObject();
                object.put("pid",deviceDTO.getPid());
                object.put("sn",deviceDTO.getSn());
                jsonArray.add(object);
            }
            jsonObject.put("deviceList",jsonArray);
            try {
                Thread.sleep(1000L);
                this.sendMessage(loginData.load(), 30010, jsonObject.toString(), true);
            } catch (Exception e) {
                log.error("发送mqtt消息失败：",e);
            }
        }

        if(this.din.equalsIgnoreCase("300000000000070741")){
            SubDeviceDTO subDeviceDTO = new SubDeviceDTO();
            subDeviceDTO.setPid("1701000303");
            subDeviceDTO.setSn("limyetingchechang002");
            SubDeviceDTO subDeviceDTO2 = new SubDeviceDTO();
            subDeviceDTO2.setPid("1701000317");
            subDeviceDTO2.setSn("limyezhaji002");
            List<SubDeviceDTO> list = new ArrayList<>();
            list.add(subDeviceDTO);
            list.add(subDeviceDTO2);

            JSONObject jsonObject = new JSONObject();
            jsonObject.put("page",1);
            jsonObject.put("pagesize",list.size());
            jsonObject.put("total",list.size());
            JSONArray jsonArray = new JSONArray();
            for(SubDeviceDTO deviceDTO:list){
                JSONObject object = new JSONObject();
                object.put("pid",deviceDTO.getPid());
                object.put("sn",deviceDTO.getSn());
                jsonArray.add(object);
            }
            jsonObject.put("deviceList",jsonArray);
            try {
                Thread.sleep(1000L);
                this.sendMessage(loginData.load(), 30010, jsonObject.toString(), true);
            } catch (Exception e) {
                log.error("发送mqtt消息失败：",e);
            }
        }

        //300000000000073199
        if(this.din.equalsIgnoreCase("300000000000073199")){
            SubDeviceDTO subDeviceDTO = new SubDeviceDTO();
            subDeviceDTO.setPid("1701000317");
            subDeviceDTO.setSn("limyezhaji003");
            SubDeviceDTO subDeviceDTO2 = new SubDeviceDTO();
            subDeviceDTO2.setPid("1701000317");
            subDeviceDTO2.setSn("limyezhaji004");
            List<SubDeviceDTO> list = new ArrayList<>();
            list.add(subDeviceDTO);
            list.add(subDeviceDTO2);

            JSONObject jsonObject = new JSONObject();
            jsonObject.put("page",1);
            jsonObject.put("pagesize",list.size());
            jsonObject.put("total",list.size());
            JSONArray jsonArray = new JSONArray();
            for(SubDeviceDTO deviceDTO:list){
                JSONObject object = new JSONObject();
                object.put("pid",deviceDTO.getPid());
                object.put("sn",deviceDTO.getSn());
                jsonArray.add(object);
            }
            jsonObject.put("deviceList",jsonArray);
            try {
                Thread.sleep(1000L);
                this.sendMessage(loginData.load(), 30010, jsonObject.toString(), true);
            } catch (Exception e) {
                log.error("发送mqtt消息失败：",e);
            }
        }

    }

    /**
     * 定时请求子设备列表
     */
    public void scheduleGetSubDevice(){
        new Thread(()->{
            try {
                while (this.subDeviceDTOList.isEmpty()){
                    Thread.sleep(12000L);
                    requestSubDevice();
                    Thread.sleep(180000L);
                }
            } catch (Exception e){
                log.error("发送mqtt消息失败：",e);
               // System.exit(0);
            }
        }).start();


    }

    /**
     * 定时发送子设备心跳
     */
    public void scheduleSendSubDeviceHeart(){
        CrontabUtil.addTaskInRate(()->{
            try {
                if(!this.subDeviceDTOList.isEmpty()){
                    /**
                     * a)	din: 视频网关的din
                     * b)	timestamp: 当前时间，自1970年1月1日0时起的毫秒数
                     * c)	message_id：30007
                     * d)	message_content： 子设备状态列表，Json字符串，包含以下信息
                     * i.	page: 1
                     * ii.	pagesize: 1
                     * iii.	total: 本次上报设备数
                     * iv.	deviceList: Json Array，每个元素为一个Json object，含两个字段：
                     * 1.	din: 摄像头din
                     * 2.	status: 状态，字符串，取值：online – 在线，offline – 离线
                     * e)	msgType： data_report
                     */
                    JSONObject jsonObject = new JSONObject();
                    jsonObject.put("page",1);
                    jsonObject.put("pagesize",1);
                    jsonObject.put("total",subDeviceDTOList.size());
                    JSONArray jsonArray = new JSONArray();
                    for(SubDeviceDTO deviceDTO:subDeviceDTOList){
                        JSONObject object = new JSONObject();
                        object.put("din",deviceDTO.getDin());
                        object.put("status",deviceDTO.getStatus());
                        jsonArray.add(object);
                    }
                    jsonObject.put("deviceList",jsonArray);
                    this.sendMessage(loginData.load(), 30002, jsonObject, true);
                }
            } catch (Exception e){
                log.error("发送mqtt消息失败：",e);
                //System.exit(0);
            }
        },20000,true);
    }
}
